/*
 * $Id: io.c 7557b5d4d82b 2012/12/02 09:07:16 pgurumur $
 *
 * Copyright (c) 2006, 2007, 2008 Prabhu Gurumurthy <pgurumur@gmail.com>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include "sshc.h"

void
blocking(int fd, int block)
{
   logit(0, "in %s", __func__);
   int val = fcntl(fd, F_GETFL);
   if (val < 0)
      error(1, "%s fcntl(F_GETFL): %d", __func__, fd);

   if (block) {
      if (val & O_NONBLOCK)
         val &= ~O_NONBLOCK;

      logit(0, "descriptor %d to be set blocking", fd);

   }
   else {
      if (!(val & O_NONBLOCK))
         val |= O_NONBLOCK;

      logit(0, "descriptor %d to be set non blocking", fd);
   }

      
   if (fcntl(fd, F_SETFL, val) < 0)
      error(1, "%s fcntl(F_SETFL): %d", __func__, fd);
}

int
wait_fd(int sock, int file)
{
   int retval = 0;
   struct timeval tv;
   fd_set fd;

   FD_ZERO(&fd);
   FD_SET((unsigned int)sock, &fd);

   memset(&tv, 0, sizeof(tv));
   if (sock == 0 || file == 0) {
      tv.tv_sec = 0;
      tv.tv_usec = 100;

      if (select(sock + 1, &fd, NULL, NULL, &tv) < 0)
         error(1, "select: %d", sock);

      if (FD_ISSET((unsigned int)sock, &fd))
         retval = 1;
   }
   else if (sock > 2) {
      fd_set *readfd = NULL;
      fd_set *writefd = NULL;

      tv.tv_sec = 0;
      tv.tv_usec = 200;

      int dir = libssh2_session_block_directions(sshc->session);
      if (dir & LIBSSH2_SESSION_BLOCK_INBOUND)
         readfd = &fd;
   
      if (dir & LIBSSH2_SESSION_BLOCK_OUTBOUND)
         writefd = &fd;

      retval = select(sock + 1, readfd, writefd, NULL, &tv);
      if (retval < 0)
         error(1, "select: %d", sock);
   }

   return retval;
}

int
ssh_read(int rsock, int wsock, int offset)
{
   char buf[MAXLINE];
   struct pollfd rpoll[1];
   int rfd = 0;
   int length = 0;
   int retval = 0;

   rpoll[0].fd = rsock;
   rpoll[0].events = POLLIN;

   if (offset < 0)
      offset = 0;

   int flag = 1;
   while (flag) {
      memset(buf, '\0', MAXLINE);
      rfd = poll(rpoll, 1, 1);
      if (rfd == 0) 
         flag = 0;
      else {
         if ((rfd == -1) || (rpoll[0].revents & (POLLERR | POLLHUP | POLLNVAL)))
            error(1, "%s rpoll()", __func__);

         length = libssh2_channel_read(sshc->channel, buf, sizeof(buf));
         if (length < 0) {
            if (errno == EAGAIN)
               continue;
            else
               error(1, "%s read()", __func__);
         }
         else if (length == 0)
            continue;
         else
            retval += length;
      }

      if (length > 0) {
         int wrlen = write(wsock, &buf[offset], length);
         if (wrlen < 0)
            error(1, "%s write()", __func__);
      }

      length = 0;
   }

   return retval;
}

int
ssh_write(int wsock, int rsock)
{
   char buf[1];
   struct pollfd wpoll[1];
   int rfd = 0;
   int length = 0;
   int retval = 0;

   wpoll[0].fd = wsock;
   wpoll[0].events = POLLOUT;

   int flag = 1;
   while (flag) {
      memset(buf, '\0', sizeof(buf));
      length = read(rsock, buf, sizeof(buf));
      if (length < 0) 
         error(1, "%s read()", __func__);
      else if (errno == EAGAIN)
         flag = 0;
      else if (length == 0)
         flag = 0;
      else
         retval += length;

      rfd = poll(wpoll, 1, 1);
      if (rfd == 0)
         flag = 0;
      else {
         if ((rfd == -1) || (wpoll[0].revents & (POLLERR | POLLHUP | POLLNVAL)))
            error(1, "%s wpoll()", __func__);

         if (wpoll[0].revents & POLLOUT) {
            if (length > 0) {
               int wrlen = libssh2_channel_write(sshc->channel, buf, length);
               if (wrlen < 0)
                  error(1, "%s write()", __func__);
   
               assert(wrlen == length);
            }
         }
         else
            flag = 0;
      }
   }

   return retval;
}

void
ssh_send_eof()
{
   int flag = 1;
   while (flag) {
      int retval = libssh2_channel_send_eof(sshc->channel);
      if (retval == LIBSSH2_ERROR_EAGAIN)
         retval = wait_fd(sshc->sock, 0);
      else if (retval == 0)
         flag = 0;
      else if (errno == EAGAIN)
         continue;
      else if (retval < 0)
         error(0, "libssh2_channel_send_eof()");
   }
}
